﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace GrapeCity.ColinHan.Reflactor
{
    public class AssemblyVisitor
    {
        public AssemblyVisitor()
        {
            this.IncludeStatic = true;
            this.IncludeProtected = true;
            this.IncludeInternal = false;
            this.IncludePrivate = false;
            this.IncludeCompilerGenerated = false;
            this.IncludeInherited = false;
        }

        public bool IncludeStatic { get; set; }
        public bool IncludeProtected { get; set; }
        public bool IncludeInternal { get; set; }
        public bool IncludePrivate { get; set; }
        public bool IncludeCompilerGenerated { get; set; }
        public bool IncludeInherited { get; set; }

        private BindingFlags BindingFlags
        {
            get
            {
                BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
                if (this.IncludeStatic)
                {
                    flags |= BindingFlags.Static;
                }
                if (!this.IncludeInherited)
                {
                    flags |= BindingFlags.DeclaredOnly;
                }
                if (this.IncludeProtected | this.IncludePrivate | this.IncludeInternal)
                {
                    flags |= BindingFlags.NonPublic;
                }
                return flags;
            }
        }

        public void Visit(Assembly assembly)
        {
            this.VisitAssembly(assembly);
        }

        protected object[] GetPropertyAttributes(System.Reflection.PropertyInfo item)
        {
            List<object> attributes = new List<object>(item.GetCustomAttributes(true));

            Type baseType = item.DeclaringType.BaseType;
            while (baseType != null && baseType != typeof(object))
            {
                PropertyInfo p = baseType.GetProperty(item.Name, this.BindingFlags, null, item.PropertyType, new Type[0], new ParameterModifier[0]);
                if (p != null)
                {
                    attributes.AddRange(p.GetCustomAttributes(true));
                }
                baseType = baseType.BaseType;
            }

            return attributes.ToArray();
        }

        protected virtual bool FilterModule(Module item)
        {
            if (!FilterAttribute(item))
            {
                return false;
            }
			
            return true;
        }
        protected virtual bool FilterType(Type item)
        {
            if (!FilterAttribute(item))
            {
                return false;
            }

            if (item.IsPublic)
            {
                return true;
            }
            else if (this.IncludeInternal && item.IsNotPublic)
            {
                return true;
            }
            return false;
        }
        protected virtual bool FilterConstructor(ConstructorInfo item)
        {
            return FilterMethodCore(item);
        }
        protected virtual bool FilterNestedType(Type item)
        {
            if (!FilterAttribute(item))
            {
                return false;
            }

            if (this.IncludeInternal && (item.IsNestedAssembly || item.IsNestedFamORAssem))
            {
                return true;
            }
            else if (this.IncludeProtected && (item.IsNestedFamily || item.IsNestedFamORAssem))
            {
                return true;
            }
            else if (this.IncludeProtected && this.IncludeInternal && item.IsNestedFamANDAssem)
            {
                return true;
            }
            else if (this.IncludePrivate)
            {
                return true;
            }
            return false;
        }
        protected virtual bool FilterField(FieldInfo item)
        {
            if (!FilterAttribute(item))
            {
                return false;
            }

            if (item.IsPublic)
            {
                return true;
            }
            else if (this.IncludeInternal && item.IsAssembly)
            {
                return true;
            }
            else if (this.IncludeProtected && (item.IsFamily || item.IsFamilyOrAssembly))
            {
                return true;
            }
            else if (this.IncludeInternal && (item.IsAssembly || item.IsFamilyOrAssembly))
            {
                return true;
            }
            else if (this.IncludeInternal && this.IncludeProtected && item.IsFamilyAndAssembly)
            {
                return true;
            }
            else if (this.IncludePrivate)
            {
                return true;
            }
            return false;
        }
        protected virtual bool FilterMethod(MethodInfo item)
        {
            return FilterMethodCore(item);
        }
        protected bool FilterMethodCore(MethodBase item)
        {
            if (!FilterAttribute(item))
            {
                return false;
            }

            if (item.IsPublic)
            {
                return true;
            }
            else if (this.IncludeInternal && item.IsAssembly)
            {
                return true;
            }
            else if (this.IncludeProtected && (item.IsFamily || item.IsFamilyOrAssembly))
            {
                return true;
            }
            else if (this.IncludeInternal && (item.IsAssembly || item.IsFamilyOrAssembly))
            {
                return true;
            }
            else if (this.IncludeInternal && this.IncludeProtected && item.IsFamilyAndAssembly)
            {
                return true;
            }
            else if (this.IncludePrivate)
            {
                return true;
            }
            return false;
        }
        protected virtual bool FilterProperty(PropertyInfo item)
        {
            if (!FilterAttribute(item))
            {
                return false;
            }

            if (item.CanRead && FilterMethodCore(item.GetGetMethod(true)))
            {
                return true;
            }
            else if (item.CanWrite && FilterMethodCore(item.GetSetMethod(true)))
            {
                return true;
            }
            return false;
        }
        protected virtual bool FilterEvent(EventInfo item)
        {
            if (!FilterAttribute(item))
            {
                return false;
            }

            return FilterMethodCore(item.GetAddMethod(true));
        }
        protected virtual bool FilterAttribute(ICustomAttributeProvider attributeProvider)
        {
            if (IncludeCompilerGenerated)
            {
                return true;
            }

            foreach(var attr in attributeProvider.GetCustomAttributes(true))
            {
                if (attr.GetType().FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
                {
                    return false;
                }
                else if (attr.GetType().FullName == "System.CodeDom.Compiler.GeneratedCodeAttribute")
                {
                    return false;
                }
            }

            return true;
        }

        protected virtual void VisitAssembly(Assembly assembly)
        {
            foreach (var item in assembly.GetModules())
            {
                if (FilterModule(item))
                {
                    this.VisitModule(item);
                }
            }
        }
        protected virtual void VisitModule(Module module)
        {
            foreach (var item in module.GetTypes())
            {
                if (item.IsNested)
                {
                    continue;
                }

                if (FilterType(item))
                {
                    this.VisitTypeDispatch(item);
                }
            }
        }
        private void VisitTypeDispatch(Type type)
        {
            if (type.IsClass)
            {
                this.VisitClass(type);
            }
            else if (type.IsValueType)
            {
                this.VisitStruct(type);
            }
            else if (type.IsInterface)
            {
                this.VisitInterface(type);
            }
            else if (type.IsEnum)
            {
                this.VisitEnum(type);
            }
            else if (typeof(Delegate).IsAssignableFrom(type))
            {
                this.VisitDelegate(type);
            }
        }
        protected virtual void VisitType(Type type)
        {
            foreach (var item in type.GetConstructors(this.BindingFlags))
            {
                if (FilterConstructor(item))
                {
                    this.VisitConstructor(item);
                }
            }
            foreach (var item in type.GetNestedTypes(this.BindingFlags))
            {
                if (FilterNestedType(item))
                {
                    this.VisitTypeDispatch(item);
                }
            }
            foreach (var item in type.GetFields(this.BindingFlags))
            {
                if (FilterField(item))
                {
                    VisitField(item);
                }
            }
            foreach (var item in type.GetProperties(this.BindingFlags))
            {
                if (FilterProperty(item))
                {
                    VisitProperty(item);
                }
            }
            foreach (var item in type.GetMethods(this.BindingFlags))
            {
                if (FilterMethod(item))
                {
                    VisitMethod(item);
                }
            }
            foreach (var item in type.GetEvents(this.BindingFlags))
            {
                if (FilterEvent(item))
                {
                    VisitEvent(item);
                }
            }
        }
        protected virtual void VisitClass(Type type)
        {
            VisitType(type);
        }
        protected virtual void VisitEnum(Type type)
        {
            VisitType(type);
        }
        protected virtual void VisitInterface(Type type)
        {
            VisitType(type);
        }
        protected virtual void VisitStruct(Type type)
        {
            VisitType(type);
        }
        protected virtual void VisitDelegate(Type type)
        {
            VisitType(type);
        }
        protected virtual void VisitMember(MemberInfo member)
        {
        }
        protected virtual void VisitConstructor(ConstructorInfo ctor)
        {
            VisitMember(ctor);
        }
        protected virtual void VisitMethod(MethodInfo method)
        {
            VisitMember(method);
        }
        protected virtual void VisitProperty(PropertyInfo property)
        {
            VisitMember(property);
        }
        protected virtual void VisitEvent(EventInfo @event)
        {
            VisitMember(@event);
        }
        protected virtual void VisitField(FieldInfo field)
        {
            VisitMember(field);
        }

        public virtual void OnError(Exception exception)
        {
            throw exception;
        }

        public virtual void OnFinished()
        {
        }
    }
}
